<template><div><h2 id="控制器" tabindex="-1"><a class="header-anchor" href="#控制器"><span>控制器</span></a></h2>
<p>控制器是 <a href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller" target="_blank" rel="noopener noreferrer">MVC</a> 模式中的一部分，是继承 <code v-pre>yii\base\Controller</code> 类的对象，负责处理请求和生成响应。具体来说，控制器从 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/structure-applications.md" target="_blank" rel="noopener noreferrer">应用主体</a> 接管控制后会分析请求数据并传送到 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/structure-models.md" target="_blank" rel="noopener noreferrer">模型</a>，传送模型结果到 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/structure-views.md" target="_blank" rel="noopener noreferrer">视图</a>，最后生成输出响应信息。</p>
<h2 id="动作" tabindex="-1"><a class="header-anchor" href="#动作"><span>动作</span></a></h2>
<p>控制器由 <em>动作</em> 组成，它是执行终端用户请求的最基础的单元，一个控制器可有一个或多个动作。如下示例，<code v-pre>post</code> 控制器包含 <code v-pre>view</code> and <code v-pre>create</code> 两个动作：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">namespace</span> <span class="token package">app<span class="token punctuation">\</span>controllers</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">Yii</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">app<span class="token punctuation">\</span>models<span class="token punctuation">\</span>Post</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>web<span class="token punctuation">\</span>Controller</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>web<span class="token punctuation">\</span>NotFoundHttpException</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">PostController</span> <span class="token keyword">extends</span> <span class="token class-name">Controller</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">actionView</span><span class="token punctuation">(</span><span class="token variable">$id</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$model</span> <span class="token operator">=</span> <span class="token class-name static-context">Post</span><span class="token operator">::</span><span class="token function">findOne</span><span class="token punctuation">(</span><span class="token variable">$id</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$model</span> <span class="token operator">===</span> <span class="token constant">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">NotFoundHttpException</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">render</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'view'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span></span>
<span class="line">            <span class="token string single-quoted-string">'model'</span> <span class="token operator">=></span> <span class="token variable">$model</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">actionCreate</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$model</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Post</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$model</span><span class="token operator">-></span><span class="token function">load</span><span class="token punctuation">(</span><span class="token class-name static-context">Yii</span><span class="token operator">::</span><span class="token variable">$app</span><span class="token operator">-></span><span class="token property">request</span><span class="token operator">-></span><span class="token function">post</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token variable">$model</span><span class="token operator">-></span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">            <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">redirect</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'view'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'id'</span> <span class="token operator">=></span> <span class="token variable">$model</span><span class="token operator">-></span><span class="token property">id</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span></span>
<span class="line">            <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">render</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'create'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span></span>
<span class="line">                <span class="token string single-quoted-string">'model'</span> <span class="token operator">=></span> <span class="token variable">$model</span><span class="token punctuation">,</span></span>
<span class="line">            <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token punctuation">}</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>在动作 <code v-pre>view</code>（定义为 <code v-pre>actionView()</code> 方法）中，代码首先根据请求参数 ID 加载模型，如果加载成功，会渲染名称为 <code v-pre>view</code> 的视图并显示，否则会抛出一个异常。</p>
<p>在动作 <code v-pre>create</code>（定义为 <code v-pre>actionCreate()</code> 方法）中, 代码类似，先将请求数据填入模型，然后保存模型，如果两者都成功，会跳转到 ID 为新创建的模型的 <code v-pre>view</code> 动作，否则显示提供用户输入的 <code v-pre>create</code> 视图。</p>
<h2 id="路由" tabindex="-1"><a class="header-anchor" href="#路由"><span>路由</span></a></h2>
<p>终端用户通过所谓的<em>路由</em>寻找到动作，路由是包含以下部分的字符串：</p>
<ul>
<li>模块 ID: 仅存在于控制器属于非应用的 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/structure-modules.md" target="_blank" rel="noopener noreferrer">模块</a>;</li>
<li>控制器 ID: 同应用（或同模块如果为模块下的控制器）下唯一标识控制器的字符串;</li>
<li>动作 ID: 同控制器下唯一标识动作的字符串。</li>
</ul>
<p>路由使用如下格式:</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line">ControllerID<span class="token operator">/</span>ActionID</span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>如果属于模块下的控制器，使用如下格式：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line">ModuleID<span class="token operator">/</span>ControllerID<span class="token operator">/</span>ActionID</span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>如果用户的请求地址为 <code v-pre>http://hostname/index.php?r=site/index</code>, 会执行 <code v-pre>site</code> 控制器的 <code v-pre>index</code> 动作。更多关于处理路由的详情请参阅 <a href="https://learnku.com/docs/yii-framework/2.0.x/routes/11998" target="_blank" rel="noopener noreferrer">路由</a> 一节。</p>
<h2 id="创建控制器" tabindex="-1"><a class="header-anchor" href="#创建控制器"><span>创建控制器</span></a></h2>
<p>在 Web 应用中，控制器应继承 <code v-pre>yii\web\Controller</code> 或它的子类。同理在控制台应用中，控制器继承 <code v-pre>yii\console\Controller</code> 或它的子类。如下代码定义一个 <code v-pre>site</code> 控制器：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">namespace</span> <span class="token package">app<span class="token punctuation">\</span>controllers</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>web<span class="token punctuation">\</span>Controller</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">SiteController</span> <span class="token keyword">extends</span> <span class="token class-name">Controller</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="控制器-id" tabindex="-1"><a class="header-anchor" href="#控制器-id"><span>控制器 ID</span></a></h3>
<p>通常情况下，控制器用来处理请求有关的资源类型，因此控制器 ID 通常为和资源有关的名词。例如，使用 <code v-pre>article</code> 作为处理文章的控制器 ID。</p>
<p>控制器 ID 应仅包含英文小写字母、数字、下划线、中横杠和正斜杠。例如 <code v-pre>article</code> 和 <code v-pre>post-comment</code> 是真是的控制器 ID，而 <code v-pre>article?</code>, <code v-pre>PostComment</code>, <code v-pre>admin\post</code> 不是。</p>
<p>控制器 ID 可包含子目录前缀，例如 <code v-pre>admin/article</code> 代表 [[yii\base\Application::controllerNamespace]] 控制器命名空间下 <code v-pre>admin</code>子目录中 <code v-pre>article</code> 控制器。子目录前缀可为英文大小写字母、数字、下划线、正斜杠，其中正斜杠用来区分多级子目录(如 <code v-pre>panels/admin</code>)。</p>
<h3 id="控制器类命名" tabindex="-1"><a class="header-anchor" href="#控制器类命名"><span>控制器类命名</span></a></h3>
<p>控制器 ID 遵循以下规则衍生控制器类名：</p>
<ol>
<li>将用正斜杠区分的每个单词第一个字母转为大写。注意如果控制器 ID 包含正斜杠，只将最后的正斜杠后的部分第一个字母转为大写；</li>
<li>去掉中横杠，将正斜杠替换为反斜杠;</li>
<li>增加 <code v-pre>Controller</code> 后缀;</li>
<li>在前面增加 [[yii\base\Application::controllerNamespace]] 控制器命名空间.</li>
</ol>
<p>下面为一些示例，假设 [[yii\base\Application::controllerNamespace]] 控制器命名空间为 <code v-pre>app\controllers</code>:</p>
<ul>
<li><code v-pre>article</code> 对应 <code v-pre>app\controllers\ArticleController</code>;</li>
<li><code v-pre>post-comment</code> 对应 <code v-pre>app\controllers\PostCommentController</code>;</li>
<li><code v-pre>admin/post-comment</code> 对应 <code v-pre>app\controllers\admin\PostCommentController</code>;</li>
<li><code v-pre>adminPanels/post-comment</code> 对应 <code v-pre>app\controllers\adminPanels\PostCommentController</code>.</li>
</ul>
<p>控制器类必须能被 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/concept-autoloading.md" target="_blank" rel="noopener noreferrer">自动加载</a>，所以在上面的例子中，控制器 <code v-pre>article</code> 类应在 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/concept-aliases.md" target="_blank" rel="noopener noreferrer">别名</a> 为 <code v-pre>@app/controllers/ArticleController.php</code> 的文件中定义，控制器 <code v-pre>admin/post-comment</code> 应在 <code v-pre>@app/controllers/admin/PostCommentController.php</code> 文件中。</p>
<blockquote>
<p>Info: 其中一个示例 <code v-pre>admin/post-comment</code> 表示你可以将控制器放在 [[yii\base\Application::controllerNamespace]] 控制器命名空间下的子目录中，在你不想用模块的情况下给控制器分类，这种方式很有用。</p>
</blockquote>
<h3 id="控制器映射" tabindex="-1"><a class="header-anchor" href="#控制器映射"><span>控制器映射</span></a></h3>
<p>可通过在应用配置中配置 [[yii\base\Application::controllerMap]] 属性来强制上述的控制器 ID 和类名映射，通常用在使用第三方不能掌控类名的控制器上，如下所示：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'controllerMap'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token comment">// 用类名申明 "account" 控制器</span></span>
<span class="line">        <span class="token string single-quoted-string">'account'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'app\controllers\UserController'</span><span class="token punctuation">,</span></span>
<span class="line"></span>
<span class="line">        <span class="token comment">// 用配置数组申明 "article" 控制器</span></span>
<span class="line">        <span class="token string single-quoted-string">'article'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">            <span class="token string single-quoted-string">'class'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'app\controllers\PostController'</span><span class="token punctuation">,</span></span>
<span class="line">            <span class="token string single-quoted-string">'enableCsrfValidation'</span> <span class="token operator">=></span> <span class="token constant boolean">false</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="默认控制器" tabindex="-1"><a class="header-anchor" href="#默认控制器"><span>默认控制器</span></a></h3>
<p>每个应用有一个由 [[yii\base\Application::defaultRoute]] 属性指定的默认控制器，当请求没有指定路由，该属性值将作为路由使用。对于 [[yii\web\Application]] 网页应用，它的值为 <code v-pre>'site'</code>，对于 [[yii\console\Application]] 控制台应用，它的值为 <code v-pre>help</code>，所以 URL为 <code v-pre>http://hostname/index.php</code> 表示由 <code v-pre>site</code> 控制器来处理，等价于 <code v-pre>http://hostname/index.php?r=site</code>。</p>
<p>您可以在应用配置中修改默认控制器，如下所示：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'defaultRoute'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'main'</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="创建动作" tabindex="-1"><a class="header-anchor" href="#创建动作"><span>创建动作</span></a></h2>
<p>创建动作，可简单地在控制器类中定义所谓的 <em>动作方法</em> 来完成，它必须是以 <code v-pre>action</code> 开头的 <code v-pre>public</code> 方法。它的返回值会作为响应数据发送给终端用户，如下代码定义了 <code v-pre>index</code> 和 <code v-pre>hello-world</code> 两个动作：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">namespace</span> <span class="token package">app<span class="token punctuation">\</span>controllers</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>web<span class="token punctuation">\</span>Controller</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">SiteController</span> <span class="token keyword">extends</span> <span class="token class-name">Controller</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">actionIndex</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">render</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'index'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">actionHelloWorld</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token string single-quoted-string">'Hello World'</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="动作-id" tabindex="-1"><a class="header-anchor" href="#动作-id"><span>动作 ID</span></a></h3>
<p>动作通常是用来执行资源的特定操作，因此，动作 ID 通常为动词，如 <code v-pre>view</code>, <code v-pre>update</code>等。</p>
<p>动作 ID 应仅包含英文小写字母、数字、下划线和中横杠，其中的中横杠用来分隔单词。例如, <code v-pre>view</code>, <code v-pre>update2</code>, <code v-pre>comment-post</code> 都是真实的动作 ID，而 <code v-pre>view?</code>, <code v-pre>Update</code> 不是。</p>
<p>可通过两种方式创建动作 ID，内联动作和独立动作。</p>
<h3 id="内联动作" tabindex="-1"><a class="header-anchor" href="#内联动作"><span>内联动作</span></a></h3>
<p>内联动作，指的是根据我们以上描述的，在控制器类中定义的动作方法。它的名字是可以根据动作 ID 遵循如下规则衍生：</p>
<ol>
<li>将每个单词的第一个字母转为大写;</li>
<li>去掉中横杠;</li>
<li>增加 <code v-pre>action</code> 前缀.</li>
</ol>
<p>例如 <code v-pre>index</code> 转成 <code v-pre>actionIndex</code>, <code v-pre>hello-world</code> 转成 <code v-pre>actionHelloWorld</code>。</p>
<blockquote>
<p>Note: 动作方法的名字<em>大小写敏感</em>，如果方法名称为 <code v-pre>ActionIndex</code> 不会认为是操作方法， 所以请求 <code v-pre>index</code> 操作会返回一个异常，也要注意操作方法必须是公有的，私有或者受保护的方法不能定义成内联操作。</p>
</blockquote>
<p>内联动作，因为容易创建，是最常用的动作，但是，如果你计划在不同地方复用一个相同的动作，或者你想重新分配一个动作，需要考虑定义它为<em>独立动作</em>。</p>
<h3 id="独立动作" tabindex="-1"><a class="header-anchor" href="#独立动作"><span>独立动作</span></a></h3>
<p>独立动作，一般通过继承 <code v-pre>yii\base\Action</code> 或它的子类来定义。与内联动作不同，主要用于在多个控制器中复用，或重构为一些 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/structure-extensions.md" target="_blank" rel="noopener noreferrer">扩展</a>。例如，Yii 自带的<code v-pre>yii\web\ViewAction</code> 和 <code v-pre>yii\web\ErrorAction</code> 都属于独立动作。</p>
<p>要使用独立动作，需要通过控制器中覆盖 [[yii\base\Controller::actions()]] 方法，并在<em>动作映射</em>中申明，如下例所示：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">actions</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token comment">// 用类来申明"error" 动作</span></span>
<span class="line">        <span class="token string single-quoted-string">'error'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'yii\web\ErrorAction'</span><span class="token punctuation">,</span></span>
<span class="line"></span>
<span class="line">        <span class="token comment">// 用配置数组申明 "view" 动作</span></span>
<span class="line">        <span class="token string single-quoted-string">'view'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">            <span class="token string single-quoted-string">'class'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'yii\web\ViewAction'</span><span class="token punctuation">,</span></span>
<span class="line">            <span class="token string single-quoted-string">'viewPrefix'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">''</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>如上所示，<code v-pre>actions()</code> 方法返回键为动作 ID、值为对应操作类名 或配置 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/concept-configurations.md" target="_blank" rel="noopener noreferrer">configurations</a> 的数组。与内联动作不同，独立动作 ID 可包含任意字符，只要在<code v-pre>actions()</code> 方法中申明。</p>
<p>为创建一个独立动作类，需要继承 <code v-pre>yii\base\Action</code> 或它的子类，并实现公有的名称为 <code v-pre>run()</code> 的方法, 该方法扮演的角色和动作方法类似，例如：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token php language-php"><span class="token delimiter important">&lt;?php</span></span>
<span class="line"><span class="token keyword">namespace</span> <span class="token package">app<span class="token punctuation">\</span>components</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>base<span class="token punctuation">\</span>Action</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">HelloWorldAction</span> <span class="token keyword">extends</span> <span class="token class-name">Action</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token string double-quoted-string">"Hello World"</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="动作结果" tabindex="-1"><a class="header-anchor" href="#动作结果"><span>动作结果</span></a></h3>
<p>内联动作的方法或独立动作的 <code v-pre>run()</code> 方法的返回值非常重要，它表示对应动作结果。</p>
<p>返回值可为 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/runtime-responses.md" target="_blank" rel="noopener noreferrer">yii\web\Response</a> 对象，作为响应发送给终端用户。</p>
<ul>
<li>对于 [[yii\web\Application]] Web 应用，返回值可为任意数据，它赋值给[[yii\web\Response::data]]，最终转换为字符串来展示响应内容。</li>
<li>对于 [[yii\console\Application]] 控制台应用，返回值可为整数，表示命令行下执行的 [[yii\console\Response::exitStatus]] 退出状态。</li>
</ul>
<p>在之前的例子中，动作结果都为字符串，并作为响应数据发送给终端用户。下例显示一个操作通过返回响应对象（因为 [[yii\web\Controller::redirect()]] 方法返回一个响应对象）可将用户浏览器跳转到新的 URL。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">actionForward</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// 用户浏览器跳转到 http://example.com</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">redirect</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'http://example.com'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="动作参数" tabindex="-1"><a class="header-anchor" href="#动作参数"><span>动作参数</span></a></h3>
<p>内联动作的方法和独立动作的 <code v-pre>run()</code> 方法都可以带参数，称为<em>动作参数</em>。参数值从请求中获取，对于 [[yii\web\Application]] Web 应用，每个动作参数的值从 <code v-pre>$_GET</code> 中获得，参数名作为键；对于 [[yii\console\Application]] 控制台应用，动作参数对应命令行参数。</p>
<p>如下例，动作 <code v-pre>view</code> (内联动作) 申明了两个参数 <code v-pre>$id</code> 和 <code v-pre>$version</code>：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">namespace</span> <span class="token package">app<span class="token punctuation">\</span>controllers</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>web<span class="token punctuation">\</span>Controller</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">PostController</span> <span class="token keyword">extends</span> <span class="token class-name">Controller</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">actionView</span><span class="token punctuation">(</span><span class="token variable">$id</span><span class="token punctuation">,</span> <span class="token variable">$version</span> <span class="token operator">=</span> <span class="token constant">null</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token comment">// ...</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>动作参数会被不同的参数填入，如下所示：</p>
<ul>
<li><code v-pre>http://hostname/index.php?r=post/view&amp;id=123</code>: <code v-pre>$id</code> 会填入<code v-pre>123</code>， <code v-pre>$version</code> 仍为 null 空因为没有 <code v-pre>version</code> 请求参数;</li>
<li><code v-pre>http://hostname/index.php?r=post/view&amp;id=123&amp;version=2</code>: <code v-pre>$id</code> 和 <code v-pre>$version</code> 分别填入 <code v-pre>123</code> 和 <code v-pre>2</code>；</li>
<li><code v-pre>http://hostname/index.php?r=post/view</code>: 会抛出 400 异常（[[yii\web\BadRequestHttpException]]），因为请求没有提供参数给必须赋值参数 <code v-pre>$id</code>；</li>
<li><code v-pre>http://hostname/index.php?r=post/view&amp;id[]=123</code>: 会抛出 400 异常（[[yii\web\BadRequestHttpException]]），因为 <code v-pre>$id</code> 参数收到数组值 <code v-pre>['123']</code> 而不是字符串。</li>
</ul>
<p>如果你想要一个动作参数来接受数组值，你应该使用 <code v-pre>array</code> 来声明类型，如下所示：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">actionView</span><span class="token punctuation">(</span><span class="token keyword type-hint">array</span> <span class="token variable">$id</span><span class="token punctuation">,</span> <span class="token variable">$version</span> <span class="token operator">=</span> <span class="token constant">null</span><span class="token punctuation">)</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token comment">// ...</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>此时，如果请求为 <code v-pre>http://hostname/index.php?r=post/view&amp;id[]=123</code>, 参数 <code v-pre>$id</code> 会使用数组值 <code v-pre>['123']</code>，如果请求为 <code v-pre>http://hostname/index.php?r=post/view&amp;id=123</code>， 参数 <code v-pre>$id</code> 会获取相同数组值，因为无类型的 <code v-pre>123</code> 会自动转成数组。</p>
<p>上述例子主要描述 Web 应用的操作参数，对于控制台应用的传参更多详情，请参阅 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/tutorial-console.md" target="_blank" rel="noopener noreferrer">控制台命令</a>。</p>
<h3 id="默认动作" tabindex="-1"><a class="header-anchor" href="#默认动作"><span>默认动作</span></a></h3>
<p>每个控制器都有一个由 [[yii\base\Controller::defaultAction]] 属性指定的默认动作，当路由只提供了控制器 ID，会使用所请求的控制器的默认动作，且它的默认值为 <code v-pre>index</code>，如果想修改它，只需简单地在控制器类中覆盖这个属性即可，如下所示：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">namespace</span> <span class="token package">app<span class="token punctuation">\</span>controllers</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>web<span class="token punctuation">\</span>Controller</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">SiteController</span> <span class="token keyword">extends</span> <span class="token class-name">Controller</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token variable">$defaultAction</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'home'</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">actionHome</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token function">render</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'home'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="控制器生命周期" tabindex="-1"><a class="header-anchor" href="#控制器生命周期"><span>控制器生命周期</span></a></h2>
<p>处理一个请求时，应用主体会根据请求路由，解析创建一个控制器，控制器经过以下生命周期来完成请求：</p>
<ol>
<li>在控制器创建和配置后，[[yii\base\Controller::init()]] 方法会被调用。</li>
<li>控制器根据请求动作 ID 创建一个动作对象:
<ul>
<li>如果动作 ID 没有指定，会使用 [[yii\base\Controller::defaultAction]] 默认动作ID；</li>
<li>如果在 [[yii\base\Controller::actions()]] 动作映射属性中找到动作 ID，会创建一个独立动作；</li>
<li>如果动作 ID 对应动作方法，会创建一个内联动作；</li>
<li>否则会抛出 [[yii\base\InvalidRouteException]] 异常。</li>
</ul>
</li>
<li>控制器按顺序调用应用主体、模块（如果控制器属于模块）、控制器的 <code v-pre>beforeAction()</code> 方法；
<ul>
<li>如果任意一个调用返回 <code v-pre>false</code>，后面未调用的 <code v-pre>beforeAction()</code> 会跳过并且动作执行会被取消；</li>
<li>默认情况下每个 <code v-pre>beforeAction()</code> 方法会触发一个 <code v-pre>beforeAction</code> 事件，在事件中你可以追加事件处理动作；</li>
</ul>
</li>
<li>控制器执行动作:
<ul>
<li>请求数据解析和填入到动作参数；</li>
</ul>
</li>
<li>控制器按顺序调用控制器、模块（如果控制器属于模块）、应用主体的 <code v-pre>afterAction()</code> 方法；
<ul>
<li>默认情况下每个 <code v-pre>afterAction()</code> 方法会触发一个 <code v-pre>afterAction</code> 事件，在事件中你可以追加事件处理动作；</li>
</ul>
</li>
<li>应用主体获取动作结果并赋值给响应对象</li>
</ol>
<h2 id="最佳实践" tabindex="-1"><a class="header-anchor" href="#最佳实践"><span>最佳实践</span></a></h2>
<p>在设计精良的应用中，控制器很精练，包含的操作代码简短；如果你的控制器很复杂，通常意味着需要重构，转移一些代码到其他类中。</p>
<p>归纳起来，控制器</p>
<ul>
<li>可访问请求组件的数据;</li>
<li>可根据请求数据调用模型的方法和其他服务组件;</li>
<li>可使用视图构造响应;</li>
<li>不应处理应被模型处理的请求数据;</li>
<li>应避免嵌入HTML或其他展示代码，这些代码最好在视图中处理.</li>
</ul>
<blockquote>
<p>💖喜欢本文档的，欢迎点赞、收藏、留言或转发，谢谢支持！<br>
作者邮箱：zhuzixian520@126.com，github地址：<a href="https://github.com/zhuzixian520" target="_blank" rel="noopener noreferrer">github.com/zhuzixian520</a></p>
</blockquote>
</div></template>


